﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Yeqian.ExtendLibrary;

namespace Assets.Scripts.Pooling
{
    /// <summary>
    /// 分组的池子，例如 都是 Enemy 类的对象，按照 Name 分组
    /// </summary>
    /// <typeparam name="EKey">分组依据</typeparam>
    /// <typeparam name="EValue">什么类型的对象</typeparam>
    /// <typeparam name="P">同一组下用什么类型的单组池子维护</typeparam>
    public class DictionaryPooling<EKey, EValue, P> : IPooling<EKey, EValue>
    {
        public Dictionary<EKey, IPooling<EValue>> Pooling { get; protected set; }
        
        /// <summary>
        /// 什么条件下这个对象是可用的
        /// </summary>
        public Func<EValue, bool> CanUse;
        /// <summary>
        /// 设置为 被占用 的方法
        /// </summary>
        public Action<EValue> SetUsed;
        /// <summary>
        /// 设置为 可用 的方法
        /// </summary>
        public Action<EValue> SetUnused;
        /// <summary>
        /// 如果池子内对象不过，我应该用什么方法创建对象
        /// </summary>
        public Func<EKey, EValue> Generator;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="canUse">什么条件下这个对象是可用的</param>
        /// <param name="setUsed">设置为 被占用 的方法</param>
        /// <param name="setUnused">设置为 可用 的方法</param>
        /// <param name="generator">如果池子内对象不过，我应该用什么方法创建对象</param>
        public DictionaryPooling(Func<EValue, bool> canUse, Action<EValue> setUsed, Action<EValue> setUnused, Func<EKey, EValue> generator)
        {
            Pooling = new Dictionary<EKey, IPooling<EValue>>();

            this.CanUse = canUse;
            this.SetUsed = setUsed;
            this.SetUnused = setUnused;
            this.Generator = generator;
        }

        public EValue GetSpawn(EKey type)
        {
            EValue ret = default;

            if (Pooling.ContainsKey(type))
            {
                ret = Pooling[type].GetSpawn();
            }
            else
            {
                Func<EValue> generator = () => Generator(type);
                //TODO: 被创建的对象也许没有对应的构造方法，就会报错。目前只有 ListPooling 所以可用这么搞，单组池子类型多了可能会出问题
                IPooling<EValue> subPooling = Activator.CreateInstance(typeof(P), CanUse, SetUsed, SetUnused, generator) as IPooling<EValue>;

                Pooling.Add(type, subPooling);
                ret = subPooling.GetSpawn();
            }

            return ret;
        }

        public void RecycelSpawn(EValue e)
        {
            SetUnused(e);
        }

        public void RecycleAllSpawn()
        {
            //Pooling.ForeachAndModifyValue(ChangeValue);
            foreach (var item in Pooling.Keys)
            {
                Pooling[item].RecycleAllSpawn();
            }
        }

        private void ChangeValue(ref IPooling<EValue> subPooling)
        {
            subPooling.RecycleAllSpawn();
        }

        public void RecycleAllSpawn(EKey type)
        {
            Pooling[type].RecycleAllSpawn();
        }

        public void EmptyPooling()
        {
            foreach (var item in Pooling)
            {
                item.Value.EmptyPooling();
            }
        }
    }
}
